#!/usr/bin/perl -w
#
# Convert ASCII files from Wavefront OBJ format to indexed polygons in
# our AG4 3-D Object File Format (OFF), with texture section support.
#
# Usage: obj2off cube_in.obj cube_out.off
#
# generates cube_out.off, cube_out.groups
#
# a ".groups" file has a list of the vertex and face groups that are
# defined in the .obj file. The format is:
# v|f groupname
# [list of vertex/face indices]
#
# By Kolja Kaehler (kkaehler@acm.org)
#

$useTextures = 1;
$texName = "unknown";

#
# process command line switches
#

while (defined($ARGV[0]) && ($_ = $ARGV[0]) && (/^-/)) {
    shift;
    last if /^--$/;
    if (/^-h/) { printUsage(); }
    if (/^-nt/) { $useTextures = 0; }
}

$input = defined($ARGV[0]) ? $ARGV[0] : "";
$input =~ /\/*(.*).obj/ or die "No .obj file given!";
$output = defined($ARGV[1]) ? $ARGV[1] : "";
open OBJ, $input or die "Can't open input file: $!\n";
open(OFF, ">$output") or die "Can't open output file: $!\n";

# 1st pass: count vertices, faces, texture vertices;
# read texture name from material lib, if any
print STDERR "Reading $input... ";
$nv = 0;
$nvt = 0;
$nf = 0;
$haveGroups = 0;
while (<OBJ>) {
  SWITCH: {
      /^v / && do { $nv++; last SWITCH; };
      /^vt / && do { $nvt++; last SWITCH; };
      /^f / && do { $nf++; last SWITCH; };
      /^g / && do { $haveGroups = 1; last SWITCH; };
      (/^mtllib (\S+)/) && do { $libName = $1; last SWITCH; };
      (/^usemtl (\S+)/) && do {
	  if (defined($libName))
	  {
	      $mtlName = $1;
	      open MTLLIB, $libName or die "Can't open mttlib file $libName: $!\n";
	      while (<MTLLIB>) {
		  if (/^newmtl $mtlName/) {
		      while (defined($_ = <MTLLIB>) && !/^newmtl/) {
			  /^map_K. (\S+)/ && ($texName = $1);
		      }
		  }
	      }
	      close MTLLIB;
	  }
	  last SWITCH;
      };
    }
}
close OBJ;

print STDERR "$nv vertices, $nf faces.\n";
print STDERR "Writing $output...";

# open .groups file, if there are any groups in the .obj file
if ($haveGroups)
{
    $groups = $output;
    $groups =~ s/.off/.groups/;
    open GROUPS, ">$groups";
    print STDERR " creating $groups...";
}

select OFF;

# write OFF header
#print "# generated by obj2off, kkaehler\@acm.org\n";
print "OFF\n";
print "$nv $nf 0\n";

# 2nd pass: write vertex list
open OBJ, $input;
$inGroup = 0;
$vIdx = 0;
while (<OBJ>) {
  SWITCH: {
      /^v +(\S+ \S+ \S+)/ && do {
	  print $1, "\n";
	  if ($inGroup) {
	      if ($inGroup == 1)
	      {
		  $inGroup++;
		  print GROUPS "vgroup ", $groupName, "\n";
	      }
	      print GROUPS $vIdx, "\n";
	  }
	  $vIdx++;
	  last SWITCH;
      };
      /^g +(\S+)/ && do {
	  $inGroup = 1;
	  $groupName = $1;
	  last SWITCH;
      };
  };
}
close OBJ;

# 3rd pass: write faces and examine texture vertex/vertex pairs
$haveTexture = 0;
$texPerVertex = 1;
open OBJ, $input;
while (<OBJ>) {
    if (/^f */) {
	@face = split;
	shift @face;
	print $#face+1;
	foreach $n (@face) {
	    $n =~ /(\d+)\/?(\d+)?\/?(d+)?/ && print " ", $1-1;
	    if ($2) {
		$haveTexture = 1;

		# map vertex index to tex vertex index
		# note: indices in .obj file are 1-based
		if (defined($texMap[$1-1]) && $texMap[$1-1] != $2-1) {
		    $texPerVertex = 0;
		}
		else {
		    $texMap[$1-1] = $2-1;
		}
	    }
	}
	print "\n";
    }
}
close OBJ;

# 4th pass: texture section
if ($haveTexture && $useTextures) {
    print "tex $texName ";
    if ($texPerVertex) {
	print "$nv v\n";
    }
    else {
        print STDERR "Can't handle texture coordinates per-triangle-and-vertex: ignored.";
    }
	   
    # store texture coordinates by tex vertex index
    open OBJ, $input;
    $tv = 0;
    while (<OBJ>) {
	/^vt (\S+) (\S+)/ && do { $texU[$tv] = $1; $texV[$tv] = $2; $tv++; }
    }
    close OBJ;

    if ($nv != $tv) {
	print STDERR "$nv vertices, but only $tv texture vertices!\n";
    }
    for ($i = 0; $i < $nv; $i++) {
	print "$texU[$texMap[$i]] $texV[$texMap[$i]]\n";
    }
}

print STDERR " done.\n";


sub printUsage {
    print "usage: obj2off [-nt][-h] input.obj output.off\n" .
	"\t-nt: no textures; ignore any texture coordinates in the input\n" .
	"\t-h: print this message\n\n";
}
